#include "Bike.h"

using namespace std;
using namespace Ogre;
using namespace BtOgre;


#define BIT(x) (1<<(x))

enum collisiontypes {
    COL_NOTHING = 0,
    COL_ACTOR   = BIT(1),
    COL_BIKE    = BIT(2),
    COL_WALL    = BIT(3),
    COL_POWERUP = BIT(4),
    COL_LASER   = BIT(5)
};


void Bike::init(Ogre::Root* ogreRoot, SceneManager* sceneMgr, btDiscreteDynamicsWorld* phyWorld, String xmlFile) {

    Bike::init(ogreRoot, sceneMgr);

    this->phyWorld = phyWorld;
    this->mSceneMgr = sceneMgr;


    TiXmlDocument xml;
    xml.LoadFile(xmlFile.c_str());
    TiXmlElement* root = xml.RootElement();

    BtOgre::StaticMeshToShapeConverter converter(mEntity);
    mCollisionShape = converter.createConvex();

    //Calculate inertia.
    mass = StringConverter::parseReal(root->Attribute("mass"));
    btVector3 inertia;

    mCollisionShape->calculateLocalInertia(mass, inertia);

    btTransform trans;
    trans.setOrigin(BtOgre::Convert::toBullet(mNode->getPosition()));
    trans.setRotation(BtOgre::Convert::toBullet(mNode->getOrientation()));
    btMotionState* state = new btDefaultMotionState(trans);

    btRigidBody::btRigidBodyConstructionInfo CI(mass, state, mCollisionShape, inertia);
    CI.m_restitution = 0.5;
    CI.m_friction = 0.8;
    mRigidBody = new btRigidBody(CI);

    mRigidBody->setActivationState(DISABLE_DEACTIVATION);
    mRigidBody->setCollisionFlags(mRigidBody->getCollisionFlags() | btCollisionObject::CF_CUSTOM_MATERIAL_CALLBACK);

    mRigidBody->setDamping(0.0f, 0.5f);
//    mRigidBody->setDamping(0.05f, 0.4f);
//    phyWorld->addRigidBody(mRigidBody, COL_BIKE, COL_ACTOR | COL_WALL | COL_LASER);
    phyWorld->addRigidBody(mRigidBody);

    engineMax = StringConverter::parseReal(root->FirstChildElement("engine")->Attribute("max"));

//	vehicleRayCaster = new btDefaultVehicleRaycaster(phyWorld);

//    TiXmlElement* wheel = root->FirstChildElement("wheel");

//    vehicle = new btHoverVehicle(mRigidBody, new btDefaultVehicleRaycaster(phyWorld));
//	vehicle->mSpringConstant = StringConverter::parseReal(root->Attribute("sprintConstant"));
//	phyWorld->addAction(vehicle);

//    vehicle = new btRaycastVehicle(tuning, mRigidBody,  vehicleRayCaster);
//    vehicle->setCoordinateSystem(0,1,2);

//    TiXmlElement* suspension = root->FirstChildElement("suspension");

//    btVector3 wheelDir(0,-1,0);
//    btVector3 wheelAxel(-1,0,0);
//    btScalar suspensionRestLength(StringConverter::parseReal(suspension->Attribute("restLength")));
//
//	vehicle->addWheel(
//	Convert::toBullet(StringConverter::parseVector3(wheel->Attribute("pos"))),
//	wheelDir,
//    wheelAxel,
//	suspensionRestLength,
//	StringConverter::parseReal(wheel->Attribute("radius")),
//	tuning,
//	true);
//
//    wheel = wheel->NextSiblingElement();
//	vehicle->addWheel(
//	Convert::toBullet(StringConverter::parseVector3(wheel->Attribute("pos"))),
//	wheelDir,
//    wheelAxel,
//	suspensionRestLength,
//	StringConverter::parseReal(wheel->Attribute("radius")),
//	tuning,
//	false);
//
//    phyWorld->addVehicle(vehicle);

//    float	wheelFriction = BT_LARGE_FLOAT;
//    float	suspensionStiffness = StringConverter::parseReal(suspension->Attribute("stiffness"));
//    float	suspensionDamping = StringConverter::parseReal(suspension->Attribute("damping"));
//    float	suspensionCompression = StringConverter::parseReal(suspension->Attribute("compression"));
//    float	rollInfluence = 0.0f;
//
//
//		for (int i=0;i<vehicle->getNumWheels();i++)
//		{
//			btWheelInfo& wheel = vehicle->getWheelInfo(i);
//			wheel.m_suspensionStiffness = suspensionStiffness;
//			wheel.m_wheelsDampingRelaxation = suspensionDamping;
//			wheel.m_wheelsDampingCompression = suspensionCompression;
//			wheel.m_frictionSlip = wheelFriction;
//			wheel.m_rollInfluence = rollInfluence;
//		}
//
//		vehicle->resetSuspension();
//		for (int i=0;i<vehicle->getNumWheels();i++)
//		{
//			//synchronize the wheels with the (interpolated) chassis worldtransform
//			vehicle->updateWheelTransform(i,true);
//		}

}


void Bike::init(Ogre::Root* OgreRoot, SceneManager* sceneMgr) {

    Actor::init(OgreRoot);

//    if (!OgreRoot->getSceneManagerIterator().hasMoreElements())
//        OGRE_EXCEPT( Exception::ERR_INTERNAL_ERROR, "No scene manager found!", "init" );

//    SceneManager* sceneMgr = OgreRoot->getSceneManagerIterator().getNext();
    mSceneMgr = sceneMgr;

    /// Lights
    headLight = sceneMgr->createLight("HeadLight");
    headLight->setPosition(0, .85, 3.5);
    headLight->setDirection(0,-0.3,1);
    headLight->setType(Light::LT_SPOTLIGHT);
    headLight->setDiffuseColour(ColourValue(1.0f, 1.0f, 1.0f));
    headLight->setSpecularColour(ColourValue(1.0f, 1.0f, 1.0f));
    headLight->setSpotlightFalloff(100.0f);
    headLight->setCastShadows(true);
    headLight->setShadowFarDistance(50.0f);
    mNode->attachObject(headLight);


//    SceneNode* trailNode = mNode->createChildSceneNode("TrailNode", Vector3(0,0.5,-5.0));
//
//    NameValuePairList pairList;
//    pairList["numberOfChains"] = "2";
//    pairList["maxElements"] = "100";
//    RibbonTrail* trail = static_cast<RibbonTrail*>(
//        sceneMgr->createMovableObject("1", "RibbonTrail", &pairList));
//        trail->setMaterialName("Examples/LightRibbonTrail");
//
//    trail->setTrailLength(50);
//    trail->setInitialColour(0, 1.0, 1.0, 0.0);
//    trail->setColourChange(0, 0.0, 1.0, 0.0, 1.0);
//    trail->setInitialWidth(0, 1);
//    trail->setWidthChange(0, 0.33);
//    trail->addNode(trailNode);
//    sceneMgr->getRootSceneNode()->attachObject(trail);

//    backLight = sceneMgr->createLight("EngineLight");
//    backLight->setPosition(0, 0.45, -5.0);
//    backLight->setType(Light::LT_POINT);
//    backLight->setDiffuseColour(ColourValue(1.0f, 0.9f, 0.2f));
//    backLight->setSpecularColour(ColourValue(1.0f, 0.9f, 0.3f));
//    backLight->setAttenuation(20,1,0.0005,0);
//    backLight->setCastShadows(false);
//    mNode->attachObject(backLight);

//    BillboardSet* bbs = sceneMgr->createBillboardSet("YellowLightBillBoardSet");
//    Billboard* bb = bbs->createBillboard(backLight->getPosition(), trail->getInitialColour(0));
//    bb->setDimensions(3,3);
//    bbs->setMaterialName("EngineFlare");
//    mNode->attachObject(bbs);

    /// Particles
//    engineParticles = sceneMgr->createParticleSystem("Engine", "Engine");
//    SceneNode* particleNode = mNode->createChildSceneNode("Particle");
//    particleNode->attachObject(engineParticles);
//    particleNode->translate(0,.2,-4.0);
////
//    dustParticles = sceneMgr->createParticleSystem(name()+"DustParticles", "Dust");
//    dustParticles->setCastShadows(true);
//
//    circleParticles = sceneMgr->createParticleSystem(name()+"CircleParticles", "Circles");
//    circleParticles->setCastShadows(true);
//
//    groundParticleNode = sceneMgr->getRootSceneNode()->createChildSceneNode(name()+"ParticleNode");
//    groundParticleNode->attachObject(dustParticles);
//    groundParticleNode->attachObject(circleParticles);

}

Bike::Bike(SceneManager* sceneMgr) : Actor(sceneMgr, "Bike"),
    throttle(0),
    engineMax(2000.0f),
    steerLimit(0.3f),
    springConstant(200.0f)
{
//    name("Bike"+id());

    engineIncrement =
    brakeIncrement =
    steeringIncrement = 0.0f;

    verticleThrustEnabled = true;
    gyroEnabled = true;
    mFire = false;


//    dustNode = 0;
}


Bike::~Bike() {
    delete mEntity;
    delete mNode;
    delete mRigidBody;
//	delete vehicleRayCaster;
    delete vehicle;
}



bool Bike::frameStarted(const Ogre::FrameEvent &evt)
{
    Real dt = evt.timeSinceLastFrame;

//    mRigidBody->setActivationState(DISABLE_DEACTIVATION);

    throttle += (engineIncrement ? exp(-0.2*throttle) : -0.2*(exp(throttle)-exp(-throttle))) * dt;

    // Limit throttle
    throttle = Math::Clamp(throttle, 0.0f, 1.0f);

    vehicle->mEngineForce = engineMax * throttle;

    vehicle->mBrakeForce += brakeIncrement;

    if(steeringIncrement==0)
        vehicle->mSteeringValue *= .667;
    else
        vehicle->mSteeringValue += steeringIncrement * dt;

    // Limit steering
    vehicle->mSteeringValue = Math::Clamp(vehicle->mSteeringValue, -steerLimit, steerLimit);

    btTransform trans = vehicle->getWorldTransform();

    mNode->setPosition(BtOgre::Convert::toOgre(trans.getOrigin()));
    mNode->setOrientation(BtOgre::Convert::toOgre(trans.getRotation()));

    if(mFire)
        Fire();

    return true;
}


bool Bike::frameEnded(const Ogre::FrameEvent &evt)
{
    return true;
}


void Bike::Fire()
{
        const float width = .5;
        const float height = 5;

        String r = StringConverter::toString(std::rand());

        ManualObject* mo = mSceneMgr->createManualObject("laserManualObject"+r);
        mo->begin("Laser", RenderOperation::OT_TRIANGLE_STRIP);
//        mo->begin("Laser", RenderOperation::OT_TRIANGLE_STRIP);

        mo->position(-width, 0, height);
        mo->textureCoord(0,0);

        mo->position( width, 0, height);
        mo->textureCoord(1,0);

        mo->position( width, 0, -height);
        mo->textureCoord(1,1);

        mo->position(-width, 0, -height);
        mo->textureCoord(0,1);

        mo->index(0);
        mo->index(1);
        mo->index(2);
        mo->index(3);
        mo->index(0);

        mo->end();

        MeshPtr mesh = mo->convertToMesh("LaserMesh");

        SceneNode* laserNode = mSceneMgr->getRootSceneNode()->createChildSceneNode("laserNode"+r);
        laserNode->attachObject(mo);

        btVector3 forward = vehicle->getWorldTransform().getBasis() * btVector3(0,0,1);

        Vector3 offset = BtOgre::Convert::toOgre(forward)*10 + Vector3(0,1,0);
        laserNode->setPosition(getSceneNode()->getPosition() + offset);

        Quaternion q = Vector3::NEGATIVE_UNIT_Z.getRotationTo(BtOgre::Convert::toOgre(forward));
        laserNode->setOrientation(q);

        BillboardSet* bbs = mSceneMgr->createBillboardSet("LaserBillBoardSet"+r);
        Billboard* bb1 = bbs->createBillboard(Vector3::UNIT_Z*5, ColourValue::Green);
        bb1->setDimensions(3,3);
        bbs->setMaterialName("Flare2");
        laserNode->attachObject(bbs);

        btScalar laserMass = 10;
        BtOgre::StaticMeshToShapeConverter converter;
        converter.addMesh(mesh);
//        btCollisionShape* shape = converter.createBox();
        btCollisionShape* shape = converter.createSphere();
        BtOgre::RigidBodyState *state = new BtOgre::RigidBodyState(laserNode);
        btVector3 inertia;
        shape->calculateLocalInertia(laserMass, inertia);
        btRigidBody::btRigidBodyConstructionInfo rigidBodyCI(laserMass, state, shape, inertia);
        btRigidBody* body = new btRigidBody(rigidBodyCI);
        body->setCollisionFlags(body->getCollisionFlags() | btCollisionObject::CF_CUSTOM_MATERIAL_CALLBACK);
//        phyWorld->addRigidBody(body, COL_LASER , COL_BIKE | COL_WALL);
//        phyWorld->addRigidBody(body, COL_LASER , COL_BIKE | COL_WALL | COL_ACTOR);
        phyWorld->addRigidBody(body);
        body->setLinearVelocity(forward*btVector3(1,0,1)*300);
        body->setAngularFactor(0);
        body->setGravity(btVector3(0,0,0));
        mFire = false;
}
